/*
 * Created on 20/10/2012 - 12:40 PM
 */
package sembr.vtk.data.input;

import vtk.*;

/**
 *
 * @author adriano
 */

public class PCF1 extends SourceBase
{
    private vtkCylinder mainCylinder;
    private vtkPlane vertPlane;
    private vtkPlane basePlane;
    private vtkImplicitBoolean bigCylinder;
    private vtkImplicitBoolean tubes;
    private vtkImplicitBoolean device;
    private vtkSampleFunction deviceSample;
    private vtkContourFilter deviceSurface;
    private vtkDelaunay3D delaunay3D;
                           
    public PCF1(double[] center, 
                            double radius, 
                            double[] plane1Origin, 
                            double[] plane1Normal,
                            double[] plane2Origin,                             
                            double[] plane2Normal
                           )
    {
        setMainCylinder(center, radius);
        setCylinderHeightByPlanes(plane1Origin, plane1Normal, plane2Origin, plane2Normal);
        setBooleanOfBigCylinder('D');
        setBigCylinderFunctions();
        setTubes();
        setDevice();
        setDeviceSampleFunction();
        setDeviceSurface();
        //applyDelaunay3DTriangularization();
        //setTubesDieletric(1.0);
        //setCylinderDieletric(12.0);
        //appendTubesWithCylinder();
        //setDeviceScalar();           
    }
    
    private void setMainCylinder(double[] center, double radius)
    {
        mainCylinder = new vtkCylinder();
        mainCylinder.SetCenter(center);
        mainCylinder.SetRadius(radius);
    }
    
    private void setCylinderHeightByPlanes(double[] plane1Origin, 
                                           double[] plane1Normal,
                                           double[] plane2Origin,                             
                                           double[] plane2Normal)
    {
        vertPlane = new vtkPlane();
        vertPlane.SetOrigin(plane1Origin);
        vertPlane.SetNormal(plane1Normal);
                
        basePlane = new vtkPlane();
        basePlane.SetOrigin(plane2Origin);
        basePlane.SetNormal(plane2Normal);         
    }
    
    private void setBooleanOfBigCylinder(char operation)
    {
        bigCylinder = new vtkImplicitBoolean();
        
        switch (operation)
        {
            // union
            case 'U':
                bigCylinder.SetOperationTypeToUnion();
                break;
            // difference    
            case 'D':
                bigCylinder.SetOperationTypeToDifference();
                break;
            // intersection
            case 'I':
                bigCylinder.SetOperationTypeToIntersection();
                break;
            // union of magnitudes    
            case 'M':
                bigCylinder.SetOperationTypeToUnionOfMagnitudes();
                break;
            default:
                bigCylinder.SetOperationTypeToUnion();
                break;
        }       
    }
    
    private void setBigCylinderFunctions()
    {
        bigCylinder.AddFunction(mainCylinder);
    }   
    
    private void setTubes()
    {        
        double xShift;  
        double zShift;
        vtkCylinder xMirrorCurrentCylinder;       
        vtkCylinder yMirrorCurrentCylinder;
        vtkCylinder xyMirrorCurrentCylinder;
        // seventeen tubes fill the cylinder diameter queued side by side
        double tubesDiameter = (mainCylinder.GetRadius() * 2) / 17;
        double[] origin = mainCylinder.GetCenter();
                       
        vtkCylinder currentCylinder;
        //currentCylinder.SetCenter(origin);
        // central tube with bigger radius
        //currentCylinder.SetRadius(tubesDiameter);
        
        tubes = new vtkImplicitBoolean();
        tubes.SetOperationTypeToUnion();
        //tubes.AddFunction(currentCylinder);
                
        int i;
        // for central tubes
        for (i = 2; i <= 6; i = i + 2)
        {            
            // determine center
            xShift = origin[0] + i * tubesDiameter;
            
            currentCylinder = new vtkCylinder();
            xMirrorCurrentCylinder = new vtkCylinder();
                       
            currentCylinder.SetCenter(xShift, origin[1], origin[2]);
            currentCylinder.SetRadius(tubesDiameter / 2);            
            
            xMirrorCurrentCylinder.SetCenter(-xShift, origin[1], origin[2]);
            xMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
            
            tubes.AddFunction(currentCylinder);
            tubes.AddFunction(xMirrorCurrentCylinder);
        }
        
        // middle tubes displacement in z coordinate
        zShift = origin[2] + 2 * tubesDiameter;
        
        for (i = 1; i <= 5; i = i + 2) 
        {
            xShift = origin[0] + i * tubesDiameter;
            
            currentCylinder = new vtkCylinder();
            xMirrorCurrentCylinder = new vtkCylinder();
            yMirrorCurrentCylinder = new vtkCylinder();
            xyMirrorCurrentCylinder = new vtkCylinder();
                        
            currentCylinder.SetCenter(xShift, origin[1], zShift);
            currentCylinder.SetRadius(tubesDiameter / 2);
                        
            xMirrorCurrentCylinder.SetCenter(-xShift, origin[1], zShift);
            xMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            yMirrorCurrentCylinder.SetCenter(xShift, origin[1], -zShift);
            yMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            xyMirrorCurrentCylinder.SetCenter(-xShift, origin[1], -zShift);
            xyMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
            
            tubes.AddFunction(currentCylinder);
            tubes.AddFunction(xMirrorCurrentCylinder);
            tubes.AddFunction(yMirrorCurrentCylinder);
            tubes.AddFunction(xyMirrorCurrentCylinder);            
        }
      
        // second middle tubes displacement in y coodinate 
        zShift = origin[2] + 4 * tubesDiameter;
        
        for (i = 0; i <= 4; i = i + 2)
        {
            xShift = origin[0] + i * tubesDiameter;
            
            currentCylinder = new vtkCylinder();
            xMirrorCurrentCylinder = new vtkCylinder();
            yMirrorCurrentCylinder = new vtkCylinder();
            xyMirrorCurrentCylinder = new vtkCylinder();
                        
            currentCylinder.SetCenter(xShift, origin[1], zShift);
            currentCylinder.SetRadius(tubesDiameter / 2);
                       
            xMirrorCurrentCylinder.SetCenter(-xShift, origin[1], zShift);
            xMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                       
            yMirrorCurrentCylinder.SetCenter(xShift, origin[1], -zShift);
            yMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            xyMirrorCurrentCylinder.SetCenter(-xShift, origin[1], -zShift);
            xyMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                          
            tubes.AddFunction(currentCylinder);
            tubes.AddFunction(xMirrorCurrentCylinder);
            tubes.AddFunction(yMirrorCurrentCylinder);
            tubes.AddFunction(xyMirrorCurrentCylinder);                         
        }
        
        // second middle tubes displacement in y coodinate 
        zShift = origin[2] + 6 * tubesDiameter;
        
        for (i = 1; i <= 3; i = i + 2)
        {
            xShift = origin[0] + i * tubesDiameter;            
            
            currentCylinder = new vtkCylinder();
            xMirrorCurrentCylinder = new vtkCylinder();
            yMirrorCurrentCylinder = new vtkCylinder();
            xyMirrorCurrentCylinder = new vtkCylinder();
            
            currentCylinder.SetCenter(xShift, origin[1], zShift);
            currentCylinder.SetRadius(tubesDiameter / 2);
                       
            xMirrorCurrentCylinder.SetCenter(-xShift, origin[1], zShift);
            xMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            yMirrorCurrentCylinder.SetCenter(xShift, origin[1], -zShift);
            yMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            xyMirrorCurrentCylinder.SetCenter(-xShift, origin[1], -zShift);
            xyMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
            
            tubes.AddFunction(currentCylinder);
            tubes.AddFunction(xMirrorCurrentCylinder);
            tubes.AddFunction(yMirrorCurrentCylinder);
            tubes.AddFunction(xyMirrorCurrentCylinder);  
        }        
    }
    
    private void setDevice()
    {
        vtkImplicitFunctionCollection bigCylinderFunctions = bigCylinder.GetFunction();
        vtkImplicitFunctionCollection tubesFunctions = tubes.GetFunction();
        
        device = new vtkImplicitBoolean();
        device.SetOperationTypeToDifference();
        
        int totalOfBigCylinderFunctions = bigCylinderFunctions.GetNumberOfItems();
        
        for (int i = 0; i < totalOfBigCylinderFunctions; i++)
        {
            device.AddFunction((vtkImplicitFunction)bigCylinderFunctions.GetItemAsObject(i));
        }
        
        int totalOfTubesFunctions = tubesFunctions.GetNumberOfItems();
        
        for (int i = 0; i < totalOfTubesFunctions; i++)
        {
            device.AddFunction((vtkImplicitFunction)tubesFunctions.GetItemAsObject(i));
        }
        
        device.AddFunction(vertPlane);
        device.AddFunction(basePlane);
    }
    
    private void setDeviceSampleFunction()
    {
        deviceSample = new vtkSampleFunction();
        deviceSample.SetImplicitFunction(device);
        deviceSample.SetModelBounds(-3.5, 3.5, -3.5, 3.5, -3.5, 3.5);
        deviceSample.SetSampleDimensions(150, 150, 150);
        deviceSample.ComputeNormalsOff();
    }
    
    private void setDeviceSurface()
    {        
        deviceSurface = new vtkContourFilter();
        deviceSurface.SetInput(deviceSample.GetOutput());
        deviceSurface.SetValue(0, 0.0);
        deviceSurface.ComputeNormalsOn();
        deviceSurface.Update();
    }
    
    private void applyDelaunay3DTriangularization()
    {
        vtkCleanPolyData cleaner = new vtkCleanPolyData();
        cleaner.SetInputConnection(deviceSurface.GetOutputPort());
                
        delaunay3D = new vtkDelaunay3D();
        delaunay3D.SetInputConnection(cleaner.GetOutputPort());
        delaunay3D.SetAlpha(0.6);
        delaunay3D.BoundingTriangulationOn();
        //delaunay3D.SetTolerance(0.05);
        delaunay3D.Update();
    }
    
    private void setTubesDieletric(double dieletric)
    { }
    
    private void setCylinderDieletric(double dieletric)
    { }
    
    private void appendTubesWithCylinder()
    { }
    
    private void setDeviceScalar()
    { }
    
    @Override
    public vtkPolyData getPolyData()
    {
        return deviceSurface.GetOutput();
    }   
}
